package com.jyx.manage_cms.service;

import com.alibaba.fastjson.JSON;
import com.jyx.framework.domain.cms.CmsPage;
import com.jyx.framework.domain.cms.CmsSite;
import com.jyx.framework.domain.cms.CmsTemplate;
import com.jyx.framework.domain.cms.request.QueryPageRequest;
import com.jyx.framework.domain.cms.response.CmsCode;
import com.jyx.framework.domain.cms.response.CmsPageResult;
import com.jyx.framework.domain.cms.response.CmsPostPageResult;
import com.jyx.framework.domain.course.response.CourseCode;
import com.jyx.framework.exception.ExceptionCast;
import com.jyx.framework.model.response.CommonCode;
import com.jyx.framework.model.response.QueryResponseResult;
import com.jyx.framework.model.response.QueryResult;
import com.jyx.framework.model.response.ResponseResult;
import com.jyx.manage_cms.config.RabbitmqConfig;
import com.jyx.manage_cms.dao.CmsPageRepository;
import com.jyx.manage_cms.dao.CmsSiteRepository;
import com.jyx.manage_cms.dao.CmsTemplateRepository;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Service
public class PageService {
    @Autowired
    CmsPageRepository cmsPageRepository;
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    GridFsTemplate gridFsTemplate;
    @Autowired
    CmsTemplateRepository cmsTemplateRepository;
    @Autowired
    GridFSBucket gridFSBucket;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    CmsSiteRepository cmsSiteRepository;

    /**
     * 页面列表分页查询
     * @param page 页码
     * @param size 每页数据数
     * @param queryPageRequest 页面查询条件对象
     * @return QueryResponseResult 查询相应结果对象
     */
    public QueryResponseResult  findList(int page, int size, QueryPageRequest queryPageRequest){
        if(queryPageRequest == null){ //判断查询请求是否为空
            queryPageRequest = new QueryPageRequest();
        }
        //条件查询匹配
        ExampleMatcher exampleMatcher = ExampleMatcher.matching()
                .withMatcher("pageAliase", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("pageName",ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("pageType",ExampleMatcher.GenericPropertyMatchers.contains());
        //查询条件
        CmsPage cmsPage = new CmsPage();
        if(StringUtils.isNotEmpty(queryPageRequest.getSiteId())){
            cmsPage.setSiteId(queryPageRequest.getSiteId());
        }
        if(StringUtils.isNotEmpty(queryPageRequest.getPageAliase())){
            cmsPage.setPageAliase(queryPageRequest.getPageAliase());
        }
        if(StringUtils.isNotEmpty(queryPageRequest.getPageName())){
            cmsPage.setPageName(queryPageRequest.getPageName());
        }
        if(StringUtils.isNotEmpty(queryPageRequest.getPageType())){
            cmsPage.setPageType(queryPageRequest.getPageType());
        }


        Example<CmsPage> example = Example.of(cmsPage, exampleMatcher);
        //创建分页对象
        page = page-1;
        Pageable pageable = PageRequest.of(page, size);
/*       if(page<=0){
            page = 1;
        }
        if(size <= 0){
            size = 20;
        }
        //创建分页对象---过时方法
        Pageable pageable = new PageRequest(page, size);*/

        //查询得出分页对象
        Page<CmsPage> cmsPages = cmsPageRepository.findAll(example,pageable);
        //封装返回结果
        QueryResult<CmsPage> cmsPageQueryResult = new QueryResult<>();
        cmsPageQueryResult.setList(cmsPages.getContent());
        cmsPageQueryResult.setTotal(cmsPages.getTotalElements());
        return new QueryResponseResult(CommonCode.SUCCESS,cmsPageQueryResult);
    }

    /**
     * 添加页面的方法
     * @param cmsPage 页面对象
     * @return CmsPageResult 封装的页面对象结果
     */
    public CmsPageResult add(CmsPage cmsPage){
        if(cmsPage==null){
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        CmsPage cmsPage1 = cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(),
                cmsPage.getSiteId(), cmsPage.getPageWebPath());
        if(cmsPage1 != null) {
            ExceptionCast.cast(CmsCode.CMS_ADDPAGE_EXISTS);
        }
        cmsPage.setPageId(null);
        cmsPageRepository.save(cmsPage);
        //返回结果
        CmsPageResult cmsPageResult = new CmsPageResult(CommonCode.SUCCESS, cmsPage);
        return cmsPageResult;
    }

    /**
     * 通过id查找页面
     * @param id
     * @return
     */
    public CmsPage getById(String id){
        Optional<CmsPage> optional = cmsPageRepository.findById(id);
        if(optional.isPresent()){
            return optional.get();
        }
        return null;
    }

    /**
     * 修改页面
     * @param id 页面id
     * @param cmsPage 页面对象
     * @return
     */
    public CmsPageResult update(String id,CmsPage cmsPage){
        CmsPage one = this.getById(id);
        if(one!=null){
            one.setTemplateId(cmsPage.getTemplateId());
            one.setSiteId(cmsPage.getSiteId());
            one.setPageAliase(cmsPage.getPageAliase());
            one.setPageName(cmsPage.getPageName());
            one.setPageWebPath(cmsPage.getPageWebPath());
            one.setPagePhysicalPath(cmsPage.getPagePhysicalPath());
            one.setPageType(cmsPage.getPageType());
            one.setDataUrl(cmsPage.getDataUrl());
            one.setPageType(cmsPage.getPageType());
            CmsPage save = cmsPageRepository.save(one);
            if(save!=null){
                return new CmsPageResult(CommonCode.SUCCESS, save);
            }
        }
        return new CmsPageResult(CommonCode.FAIL, null);
    }

    /**
     * 删除页面
     * @param id
     * @return
     */
    public ResponseResult delete(String id) {
        CmsPage delPage = this.getById(id);
        if(delPage != null){
            cmsPageRepository.delete(delPage);
            return new ResponseResult(CommonCode.SUCCESS);
        }
        return new ResponseResult(CommonCode.FAIL);
    }

    /**
     * 获取静态化文件
     * @param pageId
     * @return
     */
    public String getPageHtml(String pageId){
        //获取模板数据
        Map model = getModelByPageId(pageId);
        if(model == null){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAURLISNULL);
        }
        //获取页面模板
        String templateContent = getTemplateByPageId(pageId);
        //执行静态化
        String html = generateHtml(templateContent,model);
        return html;
    }

    /**
     * 页面静态化(组合模板和数据并返回)
     * @param template
     * @param model
     * @return
     */
    public String generateHtml(String template, Map model){
        try {
            //生成配置类
            Configuration configuration = new Configuration(Configuration.getVersion());
            //模板加载器
            StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
            stringTemplateLoader.putTemplate("template", template);
            //配置模板加载器
            configuration.setTemplateLoader(stringTemplateLoader);
            //获取模板
            Template configurationTemplate = configuration.getTemplate("template");
            String html = FreeMarkerTemplateUtils.processTemplateIntoString(configurationTemplate,model);
            return html;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取页面模板
     * @param pageId
     * @return
     */
    public String getTemplateByPageId(String pageId){
        CmsPage cmsPage = getById(pageId);
        if(cmsPage == null){
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        String templateId = cmsPage.getTemplateId();
        if(StringUtils.isEmpty(templateId)){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }
        Optional<CmsTemplate> cmsTemplateOptional = cmsTemplateRepository.findById(templateId);
        if(cmsTemplateOptional.isPresent()){
            CmsTemplate cmsTemplate = cmsTemplateOptional.get();
            String templateFileId = cmsTemplate.getTemplateFileId();
            //获取下载对象
            GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));
            //用gridFsBucket打开下载流
            GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
            //构建GridFsResource资源对象，获取下载流
            GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
            try {
                //通过流获取文件内容（模板）
                String content = IOUtils.toString(gridFsResource.getInputStream(), "UTF-8");
                return content;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 获取页面模型数据
     * @param pageId
     * @return
     */
    public Map getModelByPageId(String pageId){
        CmsPage cmsPage = getById(pageId);
        if(cmsPage == null){
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        String dataUrl = cmsPage.getDataUrl();
        ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl,Map.class);
        Map entityBody = forEntity.getBody();
        return entityBody;
    }

    /**
     * 发布页面
     * @param pageId
     * @return
     */
    public ResponseResult postPage(String pageId) {
        String pageHtml = getPageHtml(pageId);
        if(StringUtils.isEmpty(pageHtml)){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_HTMLISNULL);
        }
        //保存静态化文件
        CmsPage cmsPage = saveHtml(pageId, pageHtml);
        //发送页面消息
        sendPostPage(pageId);
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /**
     * 发送消息
     * @param pageId
     */
    private void sendPostPage(String pageId) {
        //先校验页面是否存在
        Optional<CmsPage> cmsPageOptional = cmsPageRepository.findById(pageId);
        if(!cmsPageOptional.isPresent()){
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        CmsPage cmsPage = cmsPageOptional.get();
        //将页面id存入map并转化为Json
        Map<String, String> map = new HashMap<>();
        map.put("pageId", pageId);
        String msg = JSON.toJSONString(map);
        //获取站点id作为routingKey
        String siteId = cmsPage.getSiteId();
        //发布消息
        rabbitTemplate.convertAndSend(RabbitmqConfig.EX_ROUTING_CMS_POSTPAGE,siteId,msg);
    }

    /**
     * 保存页面
     * @param pageId
     * @param pageHtml
     * @return
     */
    private CmsPage saveHtml(String pageId, String pageHtml) {
        //检验页面是否存在
        Optional<CmsPage> cmsPageOptional = cmsPageRepository.findById(pageId);
        if(!cmsPageOptional.isPresent()){
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        CmsPage cmsPage = cmsPageOptional.get();
        //存储之前先删除旧文件
        String htmlFileId = cmsPage.getHtmlFileId();
        if(StringUtils.isNotEmpty(htmlFileId)){
            gridFsTemplate.delete(Query.query(Criteria.where("_id").is(htmlFileId)));
        }
        //保存html文件到GridFs,并获取文件id
        InputStream inputStream = null;
        try {
            inputStream = IOUtils.toInputStream(pageHtml,"UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        ObjectId objectId = gridFsTemplate.store(inputStream, cmsPage.getPageName());
        String fileId = objectId.toString();
        //将文件Id存到CmsPage的HtmlFileId里面
        cmsPage.setHtmlFileId(fileId);
        //保存页面并返回
        cmsPageRepository.save(cmsPage);
        return cmsPage;
    }

    public CmsPageResult save(CmsPage cmsPage) {
        CmsPage cmsPage1 = cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(), cmsPage.getSiteId(), cmsPage.getPageWebPath());
        if(cmsPage1!=null){
            return update(cmsPage1.getPageId(),cmsPage);
        }else {
            return add(cmsPage);
        }
    }

    /**
     * 一键发布课程
     * @param cmsPage
     * @return
     */
    public CmsPostPageResult postPageQuick(CmsPage cmsPage) {
        //添加页面
        CmsPageResult cmsPageResultSave = this.save(cmsPage);
        if(!cmsPageResultSave.isSuccess()){
            return new CmsPostPageResult(CmsCode.CMS_PAGE_ADDFAIL, null);
        }
        //获取要发布的页面id
        CmsPage cmsPage1 = cmsPageResultSave.getCmsPage();
        String pageId = cmsPage1.getPageId();
        //发布页面
        ResponseResult responseResult = this.postPage(pageId);
        if(!responseResult.isSuccess()){
            return new CmsPostPageResult(CmsCode.CMS_PAGE_PUBFAIL,null);
        }
        //得到页面url
        //页面url=站点域名+站点webpath+页面webpath+页面名称
        String siteId = cmsPage1.getSiteId();
        CmsSite cmsSite = findCmsSiteById(siteId);
        if(cmsSite==null){
            return new CmsPostPageResult(CmsCode.CMS_SITE_NOTEXISTS, null);
        }
        String siteDomain = cmsSite.getSiteDomain();
        String siteWebPath = cmsSite.getSiteWebPath();
        String webPath = cmsPage1.getPageWebPath();
        String pageName = cmsPage1.getPageName();
        String pageUrl = siteDomain+siteWebPath+webPath+pageName;
        return new CmsPostPageResult(CommonCode.SUCCESS, pageUrl);
    }

    private CmsSite findCmsSiteById(String siteId) {
        Optional<CmsSite> cmsSiteOptional = cmsSiteRepository.findById(siteId);
        if(cmsSiteOptional.isPresent()){
            return cmsSiteOptional.get();
        }
        return null;
    }
}
